/**
 * MIT License
 * 
 * Copyright (c) 2024 - present @ ebraid
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

/**
 * as the device, all drivers are abstracted as drivers structures, such as the AT24C02 driver, 
 * W25Q64 driver, etc., and the peripheral driver of the chip is also abstracted as a driver structure.
 * if you want to add a new drivers, you should use drivers register interface, and if you want to delete a drivers,
 * you should unregister it first. when you register a drivers, the drivers-name directory will be 
 * created in the '/xxx_bus/drivers/' directory.
 * 
 * for example:
 * the driver name is 'at24c02', the path will be '/i2c/drivers/at24c02'
 * the driver name is 'w25q64', the path will be '/spi/drivers/w25q64'
 */

#include <kernel/bus.h>
#include <kernel/driver.h>
#include <kernel/kobject.h>
#include <kernel/syslog.h>
#include <string.h>
#include <list.h>


/**
 * @brief check whether the driver has been registered
 * @param drv the driver
 * @return true or false, true means registered, false means not registered
 */
bool driver_is_registered(struct driver *drv)
{
    if(kobj_find_by_name(&drv->bus->kobj, drv->name) == NULL) {
        return false;
    }
    else {
        return true;
    }
}


/** 
 * @brief get the driver name
 * @param drv the driver
 * @return the driver name
 */
const char *driver_get_name(struct driver *drv)
{
    if(drv && drv->kobj.name) {
        return drv->kobj.name;
    }

    return NULL;
}


/**
 * @brief set the driver name
 * @param drv the driver
 * @param name the driver name
 * @return none
 */
void driver_set_name(struct driver *drv, const char *name)
{
    drv->name = name;
    drv->kobj.name = name;
}


/**
 * @brief find the device which match the driver
 * @param drv the driver
 * @return none
 */
static void driver_match_device(struct driver* drv)
{
    struct bus_type *bus = drv->bus;
    struct device *find_dev = NULL;
    kobj_t *kobj_node;
    struct list_node *head = &bus->dev_kobj.child;

    list_for_each_entry(kobj_node, head, kobj_t, entry) {
        
        find_dev = container_of(kobj_node, struct device, kobj);

        if(bus->match(find_dev, drv) == 0) {
            find_dev->driver = drv;
            device_create_text_attr(find_dev, "bound", (char*)drv->name);
            if(drv->bus->probe) {
                drv->bus->probe(find_dev);
            }
        }
    }
}


/**
 * @brief register the driver
 * @param drv the driver
 * @return int, 0 means success, others means failed
 */
int driver_register(struct driver *drv)
{
    if(drv == NULL) {
        return -1;
    }

    if(drv->name == NULL) {
        SYSLOG_ERROR("driver's name is not set");
        return -1;
    }

    if(drv->bus == NULL) {
        SYSLOG_ERROR("driver's bus is not set");
        return -1;
    }

    if(driver_is_registered(drv)) {
        SYSLOG_ERROR("the driver is registered");
        return -1;
    }

    kobj_init(&drv->kobj, drv->name, NULL);
    if(!kobj_add(&drv->bus->drv_kobj, &drv->kobj)){
        return -1;
    }

    /* foreach all bus device, if match, to do init device pointer */
    driver_match_device(drv);

    return 0;
}


/**
 * @brief unregister the driver
 * @param drv the driver
 * @return none
 */
void driver_unregister(struct driver *drv)
{
    if(driver_is_registered(drv)) {
        kobj_remove_self(&drv->kobj);
        return;
    }

    SYSLOG_WARN("device is not register, so it can not delete");
}
